/*
 * Rotation.cpp
 * This class is responsible for standardizing the orientation of the document.
 * Created on: 04/04/2011
 * Author: Catharine Quintans Bezerra
 */

#include "Rotation.h"

Rotation::Rotation() {
}

Rotation::~Rotation() {
}

/*
 * Responsible for rotating the image.
 * @param src The image to be rotated.
 * @param dst The rotated image.
 * @param angleThe angle that the image should rotate.
 * @param valueCvScalarAll A value used to fill outliers
 */
void Rotation::rotateImage(IplImage * src, IplImage * dst, double angle,
		double valueCvScalarAll) {
	CvMat* rot_mat = cvCreateMat(2, 3, CV_32FC1);

	double angle2 = (angle * 180) / CV_PI;
	double scale = 1;

	CvPoint2D32f center = cvPoint2D32f(src->width / 2, src->height / 2);

	cv2DRotationMatrix(center, angle2, scale, rot_mat);
	cvWarpAffine(src, dst, rot_mat, CV_WARP_FILL_OUTLIERS, cvScalarAll(
			valueCvScalarAll));
}

/*
 * Responsible for highlighting the form table.
 * @param src The original image.
 * @param dst The image with the table highlighted.
 */
void Rotation::printTable(IplImage* src, IplImage* dst) {
	for (int i = 0; i < dst->width; i++) {
		for (int j = 0; j < dst->height; j++) {
			CvScalar s = cvGet2D(dst, j, i);

			if (s.val[0] == 255 && s.val[1] == 0 && s.val[2] == 0) {
				cvSet2D(src, j, i, cvScalar(0, 0, 0));
			} else {
				cvSet2D(src, j, i, cvScalar(255, 255, 255));
			}
		}
	}
}

/*
 * Find the value of x corresponding to the point of first column of the table (traverses the image from left to right).
 * @param img The image that contains the table.
 */
int Rotation::findFirstColumn(IplImage * img) {
	int firstColumn = 0;
	for (int j = 1; j < img->width; j++) {
		double columnSum = cvGetReal2D(img, img->height - 2, j) + cvGetReal2D(
				img, 0, j - 1) - cvGetReal2D(img, img->height - 2, j - 1)
				- cvGetReal2D(img, 0, j);
		if (columnSum != 0) {
			firstColumn = j - 1;
			break;
		}
	}
	return firstColumn;
}

/*
 * Knowing the x coordinate of a point, the coordinate of the point y will be discovered.
 * @param img The image where the y coordinate will be find.
 * @param x The x coordinate.
 */
int Rotation::findYPoint(IplImage * img, int x) {
	int yPoint = 0;
	for (int i = 1; i < img->height; i++) {
		double value = cvGetReal2D(img, i, x);
		if (value != 0) {
			yPoint = i - 1;
			break;
		}
	}
	return yPoint;
}

/*
 * Find the value of y corresponding to the point of first line of the table (traverses the image from top to bottom).
 * @param img The image that contains the table.
 */
int Rotation::findFirstLine(IplImage * img) {
	int firstLine = 0;
	for (int i = 1; i < img->height; i++) {
		double somaDaColuna = cvGetReal2D(img, i, img->width - 2)
				+ cvGetReal2D(img, i - 1, 0) - cvGetReal2D(img, i - 1,
				img->width - 2) - cvGetReal2D(img, i, 0);
		if (somaDaColuna != 0) {
			firstLine = i - 1;
			break;
		}
	}
	return firstLine;
}

/*
 * Find the value of x corresponding to the point of last column of the table (traverses the image from right to left).
 * @param img The image that contains the table.
 * @param threshold The threshold determine whether the pixel corresponds, or not, the last column.
 */
int Rotation::findLastColumn(IplImage * img, int threshold) {
	int lastColumn = 0;
	for (int j = img->width - 1; j > 0; j--) {
		double xPoint = cvGetReal2D(img, img->height - 2, j) + cvGetReal2D(img,
				0, j - 1) - cvGetReal2D(img, img->height - 2, j - 1)
				- cvGetReal2D(img, 0, j);
		if (xPoint > threshold) {
			lastColumn = j;
			break;
		}
	}
	return lastColumn;
}

/*
 * Find the value of x corresponding to the point of last column of the table (traverses the image upwards).
 * @param img The image that contains the table.
 * @param threshold The threshold determine whether the pixel corresponds, or not, the last line.
 */
int Rotation::findLastLine(IplImage* img, int threshold) {
	int lastLine = 0;
	for (int i = img->height - 1; i > 0; i--) {
		double yPoint = cvGetReal2D(img, i, img->width - 2) + cvGetReal2D(img,
				i - 1, 0) - cvGetReal2D(img, i - 1, img->width - 2)
				- cvGetReal2D(img, i, 0);
		if (yPoint > threshold) {
			lastLine = i;
			break;
		}
	}
	return lastLine;
}

/*
 * Knowing the y coordinate of a point, the coordinate of the point x will be discovered.
 * @param img The image where the x coordinate will be find.
 * @param y The y coordinate.
 */
int Rotation::findXPoint(IplImage * img, int y) {
	int xPoint = 0;
	for (int i = 1; i < img->width; i++) {
		double value = cvGetReal2D(img, y, i);
		if (value != 0) {
			xPoint = i - 1;
			break;
		}
	}
	return xPoint;
}

/*
 * Discover the angle necessary for the normalization of the rotation.
 * @param xPoint1 The x coordinate of first point.
 * @param yPoint1 The y coordinate of first point.
 * @param xPoint2 The x coordinate of second point.
 * @param yPoint2 The y coordinate of second point.
 */
double Rotation::findAngle(int xPoint1, int yPoint1, int xPoint2, int yPoint2) {
	if (yPoint1 - yPoint2 > xPoint2 - xPoint1) {
		return -atan((double) (xPoint2 - xPoint1)
				/ (double) (yPoint1 - yPoint2));
	} else if (xPoint2 - xPoint1 > yPoint1 - yPoint2) {
		return atan((double) (yPoint1 - yPoint2) / (double) (xPoint2 - xPoint1));
	} else {
		return 0;
	}
}

/*
 * Rotate the image.
 * @param src The image that will be rotated. (3 channels).
 * @param dst The image rotated. (3 channels).
 * @param threshold The threshold used to find the table's coordinates.
 */
void Rotation::rotate(IplImage * src, IplImage * dst, int threshold) {
	IplImage * imageGS = cvCreateImage(cvGetSize(src), 8, 1);
	IplImage * imageCS = cvCreateImage(cvGetSize(src), 8, 3);
	IplImage * sum = cvCreateImage(cvSize(src->width + 1, src->height + 1), 32,
			1);

	cvCvtColor(src, imageGS, CV_RGB2GRAY);
	cvThreshold(imageGS, imageGS, 120, 255, CV_THRESH_BINARY);
	cvCvtColor(imageGS, imageCS, CV_GRAY2BGR);

	cvFloodFill(imageCS, cvPoint(0, 0), cvScalar(255, 0, 0));
	printTable(dst, imageCS);

	cvCvtColor(dst, imageGS, CV_RGB2GRAY);
	cvThreshold(imageGS, imageGS, 120, 255, CV_THRESH_BINARY);
	cvIntegral(imageGS, sum, NULL, NULL);

	/* calculo da primeira coluna. */
	int xPoint1 = findFirstColumn(sum);

	/* calculo do valor de y a partir do valor de x */
	int yPoint1 = findYPoint(sum, xPoint1 + 1);

	/* calculo da primeira linha. */
	int yPoint2 = findFirstLine(sum);

	/* calculo do valor de x a partir do valor de y. */
	int xPoint2 = findXPoint(sum, yPoint2 + 1);

	double angle = 0;
	angle = findAngle(xPoint1, yPoint1, xPoint2, yPoint2);

	IplImage * imageOut;
	imageOut = cvCloneImage(imageGS);

	rotateImage(imageGS, imageOut, -angle, 0);

	cvIntegral(imageOut, sum, NULL, NULL);

	xPoint1 = findFirstColumn(sum) + 1;
	yPoint2 = findFirstLine(sum);

	int xPoint3 = findLastColumn(sum, threshold);
	int yPoint4 = findLastLine(sum, threshold);

	rotateImage(src, dst, -angle, 255);

	int width = xPoint3 - xPoint1;
	int height = yPoint4 - yPoint2;

	cvSetImageROI(dst, cvRect(xPoint1, yPoint2, width, height));

	cvReleaseImage(&imageGS);
	cvReleaseImage(&imageCS);
	cvReleaseImage(&sum);
	cvReleaseImage(&imageOut);
}
